package com.bsj.media.media;
import com.bsj.media.media.resample.resampler;
import com.bsj.power.common.util.Conv;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

@Slf4j
public class FlvAudioStream extends mediaStream{
    public boolean isPacketFlvHeader; // 是否打包FLV头
    public int preTagSize; // 上一个tag大小
    public int audiotimestamps; // 音频相对时间戳
    public int st_audioRate = 23;

    public AudioDecorder2 audioDecoder = null; // 音频解码器
    // public PcmEncoderAAC3 audioEncoder3 = null; // 音频pcm编码成AAC
    public PcmEncoderAAC2 audioEncoder2 = null; // 音频pcm编码成AAC

    // pcm重采样对象
    private resampler resAudio;
    private byte[] resBuffer; // 重采样的接收buffer，长度用8192
    private int resLen; // buffer 长度

    private byte[] encOneAACNeedPcmSampleBuf = null; // 编码一帧aac，需要pcm的字节数
    private int encOneAACNeedPcmSize = 0; // 编码一帧aac，需要pcm的字节数
    private int encOneAACAlreadySize = 0; // 已经保存的字节大小

    private boolean bDisplay = false; //
    private int oneAudioFrameSize = 0; // 一帧音频大小

    private File file; // 文件对象
    private FileOutputStream output; // 文件流输出对象

    /**
     * 测试，写文件
     *
     * @param data
     * @param offset
     * @param len
     * @param key
     */
    public void writeFileSystem(byte[] data, int offset, int len, String key) {

        try {
            if (null == file) {
                String filePath = "./" + key + System.currentTimeMillis() + ".dat";
                file = new File(filePath);
                output = new FileOutputStream(file);
            }

            // FileOutputStream output = new FileOutputStream(file);
            if (output != null) {
                output.write(data, offset, len);
            }
            // output.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public FlvAudioStream(String videoType) {
        super.setVideoType(videoType);
        this.preTagSize = 0;
        this.audiotimestamps = 0;
        this.audioDecoder = new AudioDecorder2();
        this.audioEncoder2 = new PcmEncoderAAC2();
        // this.audioEncoder3 = new PcmEncoderAAC3();
        this.isPacketFlvHeader = false;

        this.resAudio = new resampler();
        this.resAudio.speex_resampler_init(1, 8000, 44100, 0, 0);
        this.resAudio.speex_resampler_skip_zeros();

        this.resLen = 8192;
        this.resBuffer = new byte[resLen];
    }

    @Override
    public void setClosed(boolean isClosed) {
        super.setClosed(isClosed);
        if (isClosed) {
            // if (audioEncoder3 != null) {
            // audioEncoder3.release();
            // audioEncoder3 = null;
            // }
            if (audioEncoder2 != null) {
                audioEncoder2.release();
                audioEncoder2 = null;
            }
            if (encOneAACNeedPcmSampleBuf != null) {
                encOneAACNeedPcmSampleBuf = null;
            }
        }
    }

    /**
     * 打包FLV头信息
     *
     * @param body
     *            原始时间
     * @param dst
     * @return
     */
    public int PacketFirstFLV(byte[] body, byte[] dst) {
        int mediaLen = 0; // 打包每帧数据长度
        int dstOffset = 0;

        mediaLen = ParserVideoData.packetFlvHeader(dst, dstOffset, 0, ParserVideoData.getVideoFrame(body, 0),
                getVideoType(), 0, 0, 0, 0, 0, ParserVideoData.getAudioType(body));
        // 前一个tag大小，减4是preTageSize存储字节数，减9是FLV头字节数
        preTagSize = mediaLen - 4 - 9;
        dstOffset += mediaLen;

        int audioType = ParserVideoData.getAudioType(body);
        if (audioType != 3) {
            if (!audioDecoder.isInit()) {
                // 还没发送音频头，先发送,设备为单通道，采用率为8000
                if (!audioDecoder.init(audioType, 1, 8000)) {
                    log.error("初始化解码器失败！");
                }
            }
            if (!audioEncoder2.isInit()) {
                if (!audioEncoder2.init(1, 44100, 64000, 1, 8000)) {
                    log.error("初始化编码器失败！");
                } else {
                    setEncOneAACNeedPcmSize(audioEncoder2.getNeedSampleNum());
                }
            }
        } else {
            // 音频是aac,不需要解码，然后编码
            st_audioRate = 64;
        }

        // 音频，打包第一个音频tag帧
        mediaLen = ParserVideoData.packetFirstAudioTag(dst, dstOffset, preTagSize, audioType);
        if (mediaLen <= 5) {
            log.warn("打包第一个音频tag帧出错！！ --key:");
            return 0;
        }
        preTagSize = mediaLen - 4;

        // 改变buffer中时间戳，preTagSize:4, tag type:1, bodysize:3
        Conv.setIntNetOrder_3(dst, dstOffset + 8, audiotimestamps);
        audiotimestamps += st_audioRate;
        dstOffset += mediaLen;

        isPacketFlvHeader = true;
        return dstOffset;
    }

    /**
     * 打包FLV头后，打包其他FLV信息
     *
     * @param body
     * @param dst
     * @return
     */
    public int PacketSecondFLV(byte[] body, byte[] dst, int dstOffset) {

        int audioOffset = 21;
        int mediaLen = body.length - audioOffset;
        dstOffset = packetAAC(body, audioOffset, mediaLen, dst, dstOffset);

        return dstOffset;
    }

    /**
     * 编码AAC，并打包音频包
     *
     * @param audioData
     * @param srcOffset
     * @param mediaLen
     * @param dst
     * @param dstOffset
     * @return
     */
    public int packetAAC(byte[] audioData, int srcOffset, int mediaLen, byte[] dst, int dstOffset) {

        if (audioDecoder.isInit() && audioEncoder2.isInit()) {
            //需要转码处理
            byte[] pcm = new byte[4096];
            // 4字节海思头
            int lenSize = ((audioData[srcOffset + 2] & 0xFF) + (audioData[srcOffset + 3] << 8 & 0xFF)) * 2;
            int audioSize = mediaLen - 4;
            int ret = 0;

            int realOffset = 0;
            if (lenSize == audioSize) {
                // 有海思头
                realOffset = 4;
            } else {
                // 没有海思头
                realOffset = 0;
                // 只打印 一次
                if (!bDisplay) {
                    log.warn("收到音频数据大小不对，hisi头显示大小：" + lenSize + " 实际大小：" + audioSize);
                    bDisplay = true;
                }
            }

            ret = audioDecoder.DecoderSamples(audioData, srcOffset + realOffset, mediaLen - realOffset, pcm);
            if (ret > 0) {
                // 重采样
                int resapmleLen = resAudio.speex_resampler_process_int(0, pcm, ret / 2, resBuffer, resLen);
                int pcmLen = resapmleLen * 2;
                // aac 编码使用dst 300*1024后面的内存
                int aacOffset = 300 * 1024;
                int pcmOffset = 0;
                // int pcmLen = ret;
                while (pcmLen > 0) {
                    if (encOneAACAlreadySize + pcmLen >= encOneAACNeedPcmSize) {
                        int needSampleCopy = encOneAACNeedPcmSize - encOneAACAlreadySize;
                        System.arraycopy(resBuffer, pcmOffset, encOneAACNeedPcmSampleBuf, encOneAACAlreadySize,
                                needSampleCopy);
                        // 拷贝满一次编码aac PCM缓冲区后，已经有的size置零
                        encOneAACAlreadySize = 0;
                        pcmOffset += needSampleCopy;
                        pcmLen -= needSampleCopy;

                        int encRet = audioEncoder2.Encodec(encOneAACNeedPcmSampleBuf, 0, encOneAACNeedPcmSize, dst,
                                aacOffset, 614400);
                        if (encRet > 0) {
                            mediaLen = ParserVideoData.packetAudioVideoData(dst, aacOffset, encRet, dst, dstOffset,
                                    (int) audiotimestamps, false, preTagSize, 0, 0);
                            preTagSize = mediaLen - 4;
                            audiotimestamps += st_audioRate;

                            dstOffset += mediaLen;
                        }
                    } else {
                        System.arraycopy(resBuffer, pcmOffset, encOneAACNeedPcmSampleBuf, encOneAACAlreadySize, pcmLen);
                        encOneAACAlreadySize += pcmLen;
                        pcmLen = 0;
                    }
                }
            }
        } else {
            // aac编码, 采样率固定16000，单声道
            int adtsHeaderLen = ParserVideoData.getAdtsAACHeaderLen(audioData, srcOffset, mediaLen);
            int aacLen = ParserVideoData.getAdtsAACRawLen(audioData, srcOffset, mediaLen);
            if (adtsHeaderLen > 0 && aacLen > 0) {
                int realLen = aacLen - adtsHeaderLen;
                mediaLen = ParserVideoData.packetAudioVideoData(audioData, srcOffset + adtsHeaderLen, realLen, dst,
                        dstOffset, (int) audiotimestamps, false, preTagSize, 16000, 1);
                preTagSize = mediaLen - 4;
                audiotimestamps += st_audioRate;
            }
            dstOffset += mediaLen;
        }

        return dstOffset;
    }

    /**
     * 打包成flv，再发送
     *
     */
    @Override
    public int PacketData(byte[] FdFdBody, int offset, int len, byte[] dstBuffer) {

        if (FdFdBody != null) {
            /*
             * 头（2） + 信令(2) +
             * */
            // FDFD数据，第16字节表示音视频类型，3表示音频，0，1表示视频帧
            if (FdFdBody[20] != 0x03) {
                return 0;
            }

            int packetLen = 0;
            // 提取FDFD协议数据的数据体，14字节+body+校验1+结束位1
            byte[] body = super.getData(FdFdBody);


            // writeFileSystem(body, 21, body.length - 21, uri);

            if (!isPacketFlvHeader) {
                // 打FLV头
                packetLen = PacketFirstFLV(body, dstBuffer);
                if (isPacketFlvHeader) {
                    packetLen = PacketSecondFLV(body, dstBuffer, packetLen);
                }
            } else {
                packetLen = PacketSecondFLV(body, dstBuffer, 0);
            }

            return packetLen;
        }

        return 0;
    }

    public int getEncOneAACNeedPcmSize() {
        return encOneAACNeedPcmSize;
    }

    public void setEncOneAACNeedPcmSize(int encOneAACNeedPcmSize) {
        if (this.encOneAACNeedPcmSize == 0) {
            this.encOneAACNeedPcmSize = encOneAACNeedPcmSize;
            encOneAACNeedPcmSampleBuf = new byte[this.encOneAACNeedPcmSize];
            log.info("aac 需要的pcm大小：" + encOneAACNeedPcmSize);
        }
    }

    public int getOneAudioFrameSize() {
        return oneAudioFrameSize;
    }

    public void setOneAudioFrameSize(int oneAudioFrameSize) {
        if (oneAudioFrameSize == 0) {
            this.oneAudioFrameSize = 164;
        } else if (oneAudioFrameSize == 1) {
            this.oneAudioFrameSize = 320;
        } else if (oneAudioFrameSize == 2) {
            this.oneAudioFrameSize = 160;
        }
    }
}
